#include "datalogger.h"
#include "ses_eeprom.h"
#include "tools/data_utils.h"
#include "ses_lcd.h"

void eeprom_init() {
	ext_eeprom_init();

	// read the first byte in eeprom
	uint8_t status = 0;

	ext_eeprom_read(DATALOGGER_END, &status, sizeof(status));

	if (status == 1) {
		uint8_t upperStatus, lowerStatus;
		upperStatus = 0;
		lowerStatus = 0;

		for (uint16_t offset = OFFSET_START + STRUCT_SIZE - 1;
				offset < OFFSET_END; offset += STRUCT_SIZE)
				{
			ext_eeprom_read(calc_abs_addr(offset, 0), &upperStatus,
					sizeof(upperStatus));
			ext_eeprom_read(calc_abs_addr(offset, -STRUCT_SIZE), &lowerStatus,
					sizeof(lowerStatus));

			if (upperStatus == FLAG_LAST_STRUCT
					&& lowerStatus == FLAG_END_STRUCT) {

				DATA.stack_offset = ((offset + 1) % OFFSET_END);
				uint8_t temp = 0;
				uint16_t co2 = 0;

				for (uint8_t i = 0; i < TABLE_SIZE; i++) {
					ext_eeprom_read(
							calc_abs_addr(DATALOGGER_START, DATA.stack_offset),
							&temp, sizeof(temp));
					DATA.temperature[i] = temp;
					ext_eeprom_read(
							calc_abs_addr(DATALOGGER_START,
									DATA.stack_offset + 1), (uint8_t*) &co2,
							sizeof(co2));
					DATA.co2[i] = co2;
				}
				return;
			}
		}
		DATA.stack_offset = OFFSET_START;
	} else {
		// the eeprom is never initiated, initiate all the cells to zero, the first byte to 1
		forceInit();
	}
}

void forceInit() {
	uint8_t status = FLAG_LAST_STRUCT;
	ext_eeprom_write(DATALOGGER_END, &status, sizeof(uint8_t));

	status = FLAG_END_STRUCT;
	for (uint32_t i = DATALOGGER_START; i < DATALOGGER_END; i++) {
		ext_eeprom_write(i, &status, sizeof(uint8_t));
	}

	DATA.stack_offset = OFFSET_START;
}

void save_to_eeprom() {

	// sort
	q_sort8(DATA.temperature, 0, TABLE_SIZE);
	q_sort16(DATA.co2, 0, TABLE_SIZE);

	// get median
	uint8_t medTemp = DATA.temperature[TABLE_MEDIAN];
	uint16_t medCO2 = DATA.co2[TABLE_MEDIAN];
	uint16_t offset = DATA.stack_offset;
	uint8_t last_struct = FLAG_LAST_STRUCT;
	uint8_t end_struct = FLAG_END_STRUCT;

	// save to eeprom avoid recovery status
	if (medCO2 != 0 && medTemp != 0) {

		// save temperature
		ext_eeprom_write(calc_abs_addr(offset, 0), &medTemp, sizeof(medTemp));

		// save CO2
		ext_eeprom_write(calc_abs_addr(offset, 1), (uint8_t*) &medCO2,
				sizeof(medCO2));

		// set the flag of the last saved struct
		ext_eeprom_write(calc_abs_addr(offset, 3), &last_struct,
				sizeof(uint8_t));

		// set the flag of the previous saved struct
		ext_eeprom_write(calc_abs_addr(offset, -1), &end_struct,
				sizeof(uint8_t));

		// reading from eeprom
		ext_eeprom_read(calc_abs_addr(offset, 0), (uint8_t *) &medTemp,
				sizeof(medTemp));
		ext_eeprom_read(calc_abs_addr(offset, 1), (uint8_t *) &medCO2,
				sizeof(medCO2));

		// update the new stack offset
		lcd_setCursor(0, 1);
		fprintf(lcdout, "extracting <-> %d", DATA.stack_offset);
		lcd_setCursor(0, 2);
		fprintf(lcdout, "Medi:%dC", medTemp);
		lcd_setCursor(12, 2);
		fprintf(lcdout, "%dppm ", medCO2);

		DATA.stack_offset += STRUCT_SIZE;
		DATA.stack_offset %= OFFSET_END;
	} else {
		lcd_setCursor(0, 2);
		fprintf(lcdout, "recovery at %d", DATA.stack_offset);
	}
}

uint32_t calc_abs_addr(uint16_t offset, int step) {
	uint16_t temp = (offset + step + OFFSET_END) % OFFSET_END;
	return DATALOGGER_START + temp;
}

void put_temperature(uint8_t temperature) {
	DATA.temp_passes++;
	DATA.temp_passes %= TABLE_SIZE;
	DATA.temperature[DATA.temp_passes] = temperature;
}

void put_co2(uint16_t co2) {
	DATA.co2_passes++;
	DATA.co2_passes %= TABLE_SIZE;
	DATA.co2[DATA.co2_passes] = co2;
}

void getAverageValues() {
	uint16_t offset = DATA.stack_offset;
	uint16_t avergCO2 = 0;
	uint16_t co2 = 0;
	uint16_t avergTemp = 0;
	uint8_t temp = 0;
	uint32_t addr = 0;

	// read from eeprom
	for (int i = -1; i >= -AVERAGE_SIZE; i--) {
		// read the i-th median temperature
		addr = calc_abs_addr(offset, i * STRUCT_SIZE);
		ext_eeprom_read(addr, &temp, sizeof(uint8_t));

		// read the i-th median co2
		addr = calc_abs_addr(offset, i * STRUCT_SIZE + 1);
		ext_eeprom_read(addr, (uint8_t *) &co2, sizeof(uint16_t));
		avergTemp += temp;
		avergCO2 += co2;
	}
	avergTemp /= AVERAGE_SIZE;
	avergCO2 /= AVERAGE_SIZE;
	DATA.avergTemp = avergTemp;
	DATA.avergCO2 = avergCO2;
}
